package app

import (
	"context"
	"fmt"
	"gitee.com/zhucheer/orange/cfg"
	"gitee.com/zhucheer/orange/logger"
	"os"
	"sync"
	"time"
)

type StopSignal int32

type exitWait struct {
	mutex        sync.Mutex
	wg           *sync.WaitGroup
	deferFuns    []func()
	stopSignList []chan StopSignal
}

var exitWaitHandler *exitWait

func init() {
	exitWaitHandler = &exitWait{
		wg: &sync.WaitGroup{},
	}
}

// ExitWaitFunDo 退出后等待处理完成
func ExitWaitFunDo(doFun func()) {
	exitWaitHandler.wg.Add(1)
	defer exitWaitHandler.wg.Done()
	if doFun != nil {
		doFun()
	}
}

// AppDefer 应用退出后置操作
func AppDefer(deferFun ...func()) {
	exitWaitHandler.mutex.Lock()
	defer exitWaitHandler.mutex.Unlock()
	for _, funcItem := range deferFun {
		if funcItem != nil {
			exitWaitHandler.deferFuns = append(exitWaitHandler.deferFuns, funcItem)
		}
	}
}

// ListenStop 订阅app退出信号
func ListenStop(stopSig chan StopSignal) {
	exitWaitHandler.mutex.Lock()
	defer exitWaitHandler.mutex.Unlock()

	exitWaitHandler.stopSignList = append(exitWaitHandler.stopSignList, stopSig)
}

// shutdownDo 退出相关操作
func shutdownDo(ctx context.Context, orangeSrv *OrangeServer) {
	// 发送应用退出信号
	sendAppStop()
	//处理后置操作等待结束
	appDeferDo(ctx)

	// 关闭http服务, 阻止新的请求进入并等待活跃连接处理完成
	orangeSrv.ShutdownDo(ctx)
}

// sendAppStop 发送应用退出信号
func sendAppStop() {
	for _, stopSigItem := range exitWaitHandler.stopSignList {
		go func(sig chan StopSignal) {
			sig <- StopSignal(1)
		}(stopSigItem)
	}
}

// appDefer 退出后处理
func appDeferDo(ctx context.Context) {
	go func() {
		maxTimeout := cfg.GetInt("app.maxWaitSecond", cfg.ConfigDef.GetInt("app.maxWaitSecond"))
		if maxTimeout == 0 {
			maxTimeout = 90
		}

		maxTimeoutContext, _ := context.WithTimeout(context.TODO(), time.Duration(maxTimeout)*time.Second)
		select {
		case <-maxTimeoutContext.Done():
			fmt.Println(fmt.Sprintf("[ORANGE] \033[0;33m app wait %vs timeout shutdown. \033[0m ", maxTimeout))
			logger.Critical("app server shutdown by timeout wait %vs.", maxTimeout)
			time.Sleep(2 * time.Second)
			os.Exit(0)
		}
	}()

	for _, funcItem := range exitWaitHandler.deferFuns {
		go func(funcDo func()) {
			exitWaitHandler.wg.Add(1)
			defer exitWaitHandler.wg.Done()
			funcDo()
		}(funcItem)
	}

}
